package jflowmap.views.heatmap;

import java.awt.Font;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import jflowmap.data.ValueStatistics;
import jflowmap.model.graph.Edge;
import jflowmap.model.ontology.Dimension;
import jflowmap.model.ontology.Measurement;
import jflowmap.model.ontology.SelectionListener;
import jflowmap.util.InformativeComparator;
import jflowmap.util.piccolo.PNodes;
import jflowmap.views.Disposable;

import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.nodes.PText;

public class SingleHeatMapRow extends PNode implements SelectionListener, Disposable {

	public static final InformativeComparator<SingleHeatMapRow> COMPARE_BY_NAME = new InformativeComparator<SingleHeatMapRow> () {
		@Override
		public int compare(SingleHeatMapRow arg0, SingleHeatMapRow arg1) {
			return arg0.edge.getSourceNode().getDimension().compareTo(
					arg1.edge.getSourceNode().getDimension());
		}

		@Override
		public String getDescription() {
			return "alphabetisch geordnet";
		}
	};
	
	public static final InformativeComparator<SingleHeatMapRow> COMPARE_BY_AVERAGE = new InformativeComparator<SingleHeatMapRow> () {
		@Override
		public int compare(SingleHeatMapRow arg0, SingleHeatMapRow arg1) {
			return arg0.stats.getAvg() > arg1.stats.getAvg() ? -1
				 : arg1.stats.getAvg() > arg0.stats.getAvg() ? 1
				 : 0;
		}

		@Override
		public String getDescription() {
			return "nach Durschnittswert gereiht (dimensionslos)";
		}
	};
	
	public static final InformativeComparator<SingleHeatMapRow> COMPARE_BY_MEASUREMENT = new InformativeComparator<SingleHeatMapRow> () {
		@Override
		public int compare(SingleHeatMapRow arg0, SingleHeatMapRow arg1) {		
			if (arg0.getMeasurement().getQualifiedName().compareTo(arg1.getMeasurement().getQualifiedName()) == 0) {
				return COMPARE_BY_NAME.compare(arg0, arg1);
			} 
			
			return arg0.getMeasurement().getQualifiedName().compareTo(arg1.getMeasurement().getQualifiedName());
		}

		@Override
		public String getDescription() {
			return "gruppiert (alphabetisch)";
		}
	};
	
	
	
	private static final Font SOURCE_NAME_FONT = new Font("Arial", Font.ITALIC, 12);
	
	private final HeatMapLayer heatMap;
	private final Edge edge;
	private final ValueStatistics stats;
	
	private Map<Dimension, HeatMapCell> cells;
	private double maxCellWidth;
	private final PText sourceName;
	private List<Dimension> visibleValueDimensions;
	
	public SingleHeatMapRow(Edge edge, HeatMapLayer heatMap) {
		this.heatMap = heatMap;
		this.edge = edge;
		this.cells = Maps.newTreeMap();
		this.stats = ValueStatistics.createFor(edge.getValues());
		
		createCells();
		
		this.sourceName = new PText(edge.getSourceNode().getName());
		this.sourceName.setFont(SOURCE_NAME_FONT);
		this.sourceName.setOffset(-sourceName.getWidth()-5, HeatMapCell.CELL_HEIGHT/2 - sourceName.getHeight()/2);
		this.addChild(this.sourceName);
	}
	
	public Edge getEdge() {
		return edge;
	}

	private void createCells() {
		for (final Dimension dimension: edge.getValueDimensions()) {
			final HeatMapCell cell = new HeatMapCell(this, edge.getValue(dimension), dimension, heatMap);
			cells.put(dimension, cell);
			
			heatMap.getView().getSelection("standard").addSelectionListener(dimension, this);
			if (heatMap.getView().getSelection("standard").isSelected("VALUE", dimension)) {
				this.addChild(cell);
			}
			
			if (cell.getFullBoundsReference().width > maxCellWidth) {
				maxCellWidth = cell.getFullBoundsReference().width;
			}
		}
	}

	public double getMaxCellWidth() {
		return maxCellWidth;
	}

	public void setCellWidth(double cellWidth) {
		this.maxCellWidth = cellWidth;
		
		for (HeatMapCell cell: cells.values()) {
			cell.setCellWidth(cellWidth);
		}
	}

	public HeatMapCell get(Dimension valueDimension) {
		return cells.get(valueDimension);
	}
	
	public int getNumberOfVisibleCells() {
		return Iterables.size(PNodes.childrenOfType(this, HeatMapCell.class));
	}
	
	public List<Dimension> getVisibleValueDimensions() {
		return visibleValueDimensions;
	}

	public void update() {
		double x = 0.0;
		final List<HeatMapCell> cells = Lists.newLinkedList(PNodes.childrenOfType(this, HeatMapCell.class));
		Collections.sort(cells, new Comparator<HeatMapCell>() {
			@Override
			public int compare(HeatMapCell cell1, HeatMapCell cell2) {
				return Dimension.COMPARE_BY_NAME.compare(cell1.getValueDimension(), cell2.getValueDimension());
			}
		});
		
		this.visibleValueDimensions = Lists.newLinkedList();
		for (HeatMapCell cell: cells) {
			this.visibleValueDimensions.add(cell.getValueDimension());
		}
		
		for (HeatMapCell cell: cells) {
			cell.update();
			cell.setOffset(x, 0);
			x += maxCellWidth;
		}
		
		heatMap.getHeader().setCellWidth(maxCellWidth);
		heatMap.getHeader().setValueDimensions(this.getVisibleValueDimensions());
	}

	@Override
	public void select(String category, Dimension dimension) {
		if ("VALUE".equals(category)) {
			this.addChild(cells.get(dimension));
		}
	}

	@Override
	public void unselect(String category, Dimension dimension) {
		if ("VALUE".equals(category)) {
			this.removeChild(cells.get(dimension));
		}
	}

	public Dimension getSourceDimension() {
		return edge.getSourceNode().getDimension();
	}
	
	public Measurement getMeasurement() {
		return Measurement.getComparableMeasurement(edge.getValues());
	}

	public void addMeasurementInfo(PInfo info) {
		String measurement = info.get(getSourceDimension().getName());
		
		if (measurement == null) {
			Measurement comparableMeasurement;
			if ((comparableMeasurement = Measurement.getComparableMeasurement(edge.getValues())) != null) {
				info.set(getSourceDimension().getName() + ":", comparableMeasurement.getQualifiedName());
			}
		}
	}

	public void removeMeasurementInfo(PInfo info) {
		info.remove(getSourceDimension().getName() + ":");
	}

	@Override
	public void dispose() {
		for (final Dimension dimension: edge.getValueDimensions()) {
			heatMap.getView().getSelection("standard").removeSelectionListener(dimension, this);
		}
		
		for (HeatMapCell cell: this.cells.values()) {
			cell.dispose();
		}
	}

}
